Skip to content

fix(runtime): display proper error when throwing in event handlers#32663

Merged
bartlomieju merged 2 commits intomainfrom
fix/unload-event-error-display
Mar 12, 2026
Merged

fix(runtime): display proper error when throwing in event handlers#32663
bartlomieju merged 2 commits intomainfrom
fix/unload-event-error-display

Conversation

@bartlomieju
Copy link
Member

@bartlomieju bartlomieju commented Mar 12, 2026

Summary

  • Fixes "Uncaught null" being displayed instead of the actual error when throwing in unload, beforeunload, load, or process exit/beforeExit event handlers
  • Root cause: reportException in JS calls op_dispatch_exception which stores the real exception in ExceptionState and terminates execution. The Rust-side dispatch functions then checked tc_scope.exception() which returned null (the termination exception) instead of the stored one
  • Fix: use exception_to_err which properly checks ExceptionState for a dispatched exception before falling back to tc_scope.exception()

Before:

error: Uncaught null

After:

error: Uncaught Error: foo
globalThis.addEventListener("unload", () => { throw new Error("foo"); });
                                                    ^
    at file:///...$deno$eval.mts:1:53
    at innerInvokeEventListeners (ext:deno_web/02_event.js:781:9)
    ...

Closes #29087

bartlomieju and others added 2 commits March 12, 2026 15:46
When throwing in `unload`, `beforeunload`, `load`, or process exit/beforeExit
event handlers, Deno displayed "Uncaught null" instead of the actual error
message and stack trace.

The root cause: when an event listener throws, the JS-side `reportException`
calls `op_dispatch_exception` which stores the real exception in
`ExceptionState` and terminates execution. The Rust-side event dispatch
functions then checked `tc_scope.exception()` which returned the termination
exception (null), not the stored one. Using `exception_to_err` instead
properly retrieves the dispatched exception.

Closes #29087

Co-Authored-By: Claude Opus 4.6 <[email protected]>
Copy link
Contributor

@kajukitli kajukitli left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lgtm

using exception_to_err() here is the right fix. these call sites were bypassing the existing dispatched-exception handling and going straight to tc_scope.exception(), so once termination had swapped in the null-ish termination exception they lost the real error.

the new tests for load / beforeunload / unload cover the browser-style event cases well.

Copy link
Contributor

@marvinhagemeister marvinhagemeister left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@bartlomieju bartlomieju merged commit 7698bcc into main Mar 12, 2026
112 checks passed
@bartlomieju bartlomieju deleted the fix/unload-event-error-display branch March 12, 2026 16:42
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Throwing in addEventListener("unload") displays null message

3 participants